import java.util.ArrayList;



// Board class that holds all the methods and positions
// for the jumper board
public class Board implements Piece {
	
	// Initialise the board and some of its variables
	public int[][] board;
	public int size;
	private int emptyAmount;
	
	public Board(int s) {
		size = s;
		board = new int[size][size];
	}
	
	// Inserts a value into the board at position [row][column]
	public void insert(int row, int col, int value) {
		board[row][col] = value;
	}
	
	// Inserts single piece into the board
	// Returns error if board position is not empty
	// Note: same as insert but with error catching
	private boolean insertSingle(int row, int col, int player) {
		if (board[row][col] != EMPTY) {
			return false;
		}
		
		board[row][col] = player;
		
		return true;
	}
	
	// Inserts move into board whilst also checking for errors
	public boolean insertMove(Move move) {
		// checks to see if move isn't empty
		if (move == null)
			return false;
		
		// checks if it is a single move
		if(move.IsPlaceMove) {
			if(insertSingle(move.RowPositions[0],move.ColPositions[0], move.P)){
				return true;
			} else {
				return false;
			}
			
		}
		
		AdjList possiblejMoves = jumpMoves(move.P);
		
		if (board[move.RowPositions[0]][move.ColPositions[0]]==EMPTY
				|| move.RowPositions.length<2 ||possiblejMoves.numVertices()==0)
			return false;
	
		int i = 0;
		
		// checks if row and col at i, have the ability to jump into row and
		// col at i+1 in array by checking if it is a valid neighbour in the
		// calculated jump moves
		while (i < move.RowPositions.length-1){
			if (possiblejMoves.isNeighbourOf(move.RowPositions[i], move.ColPositions[i], move.RowPositions[i+1], move.ColPositions[i+1])) {
				insertJump(move.RowPositions[i], move.ColPositions[i], move.RowPositions[i+1], move.ColPositions[i+1], move.P);
			} else {
				return false;
			}
			i++;
		}
		
		return true;
	}
	
	// inserts jump into [row2][col2] as well as figures out what is in the
	// piece inbetween the jump. If it isn't the players piece, the space
	// becomes dead or leaves it otherwise.
	private void insertJump(int row1, int col1, int row2, int col2, int player) {
		board[row2][col2] = player;
		int rowMiddle;
		int colMiddle;
		
		rowMiddle = (row1+row2)/2;
		colMiddle = (col1+col2)/2;
		
		if (board[rowMiddle][colMiddle] != player){
			board[rowMiddle][colMiddle] = DEAD;
		} 
		
	}
	
	// Removes the value at position [row][column]
	public void remove(int row, int col) {
		board[row][col] = EMPTY;
	}
	
	// Prints out the current configuration of the board
	public void printOut() {
		for (int i=0; i<size; i++) {
			for (int j=0; j<size; j++) {
				System.out.print(board[i][j]);
			}
			System.out.println();
		}
	}
	
	// Cycles through the board until it reaches a board value with the 
	// player piece in it. Then calls the jumpMoves method to determine
	// the jump moves at this position. Returns adjacency list created 
	// which contains the valid jump moves.
	public AdjList jumpMoves(int player) {
		AdjList list = new AdjList(size);
		
		for (int row = 0; row < size; row++) {
			for (int col = 0; col < size; col++) {
				if (board[row][col] == player)
					availableJumps(list, row, col, player);
			}
		}
		
		return list;
	}
	
	// Returns possible moves from the current board state
	// *Still need to get DFS returning jumps*
	public ArrayList<Move> possibleMoves(int player) {
		// returns graph of possible jump moves
		AdjList listMoves = jumpMoves(player);
		
		ArrayList<Move> possibleMoves = new ArrayList<Move>();
		// runs a DFS search and returns array of Moves
		Move[] jumpMoves = listMoves.DFS(player);
		
		// adds jump moves into possible moves arraylist
		if (jumpMoves!=null){
			for (int i=0; i<jumpMoves.length; i++){
				possibleMoves.add(jumpMoves[i]);
			}
		}
		
		Move singleMoves;
		int[] rowPos = new int[1];
		int[] colPos = new int[1];
		
		// Searchs board for empty places and adds each single
		// move into possible moves
		for (int row=0; row<size; row++) {
			for (int col=0; col<size; col++) {
				if (board[row][col] == EMPTY) {
					rowPos[0] = row;
					colPos[0] = col;
					singleMoves = new Move(player, true, rowPos, colPos);
					possibleMoves.add(singleMoves);
					rowPos = new int[1];
					colPos = new int[1];
				}
			}
		}

		return possibleMoves;
		
	}

	// returns the number of empty spaces on the board
	// empty amount is calculated during the create function
	public int numEmpty() {
		int numEmpty = 0;
		for (int row = 0; row < size; row++) {
			for (int col = 0; col < size; col++) {
				if (board[row][col] == EMPTY)
					numEmpty++;
			}
		}
		emptyAmount = numEmpty;
		return emptyAmount;
	}
	
	// returns the amount of the player's pieces on the board
	public int numPlayerPieces(int player) {
		int numPieces = 0;
		
		for (int i=0; i<size; i++) {
			for (int j=0; j<size; j++) {
				if (board[i][j] == player)
					numPieces++;
			}
		}
		
		return numPieces;
	}
	
	// Takes the board position at [row][col] and looks at all the adjacent places
	// and sends their values into the function canJump to see if there is a 
	// possible jump move at the position.
	// If there is a jump move, it temporarily inserts a piece in the jump
	// position and recursively calls until a jump move isn't found.
	// Removes the temporary pieces afterwards.
	// Returns false if position has already been looked at in a previous jump
	// move (saves on runtime), otherwise returns true.
	private boolean availableJumps(AdjList list, int row, int col, int player) {
		
		if (list.doesExist(row, col)) {
			return false;
		}
		
		if (canJump(list, row, col, row-1, col-1, row-2, col-2)) {
			if (list.doesExist(row-2, col-2) == false) {
				insert(row-2, col-2, player);
				availableJumps(list, row-2, col-2, player);
				remove(row-2, col-2);
			}
		}
		if (canJump(list, row, col, row-1, col, row-2, col)) {
			if (list.doesExist(row-2, col) == false) {
				insert(row-2, col, player);
				availableJumps(list, row-2, col, player);
				remove(row-2, col);
			}
		}
		if (canJump(list, row, col, row-1, col+1, row-2, col+2)) {
			if (list.doesExist(row-2, col+2) == false) {
				insert(row-2, col+2, player);
				availableJumps(list, row-2, col+2, player);
				remove(row-2, col+2);
			}
		}
		if (canJump(list, row, col, row, col-1, row, col-2)) {
			if (list.doesExist(row, col-2) == false) {
				insert(row, col-2, player);
				availableJumps(list, row, col-2, player);
				remove(row, col-2);
			}
		}
		if (canJump(list, row, col, row, col+1, row, col+2)) {
			if (list.doesExist(row, col+2) == false) {
				insert(row, col+2, player);
				availableJumps(list, row, col+2, player);
				remove(row, col+2);
			}
		}
		if (canJump(list, row, col, row+1, col-1, row+2, col-2)) {
			if (list.doesExist(row+2, col-2) == false) {
				insert(row+2, col-2, player);
				availableJumps(list, row+2, col-2, player);
				remove(row+2, col-2);
			}
		}
		if (canJump(list, row, col, row+1, col, row+2, col)) {
			if (list.doesExist(row+2, col) == false) {
				insert(row+2, col, player);
				availableJumps(list, row+2, col, player);
				remove(row+2, col);
			}
		}
		if (canJump(list, row, col, row+1, col+1, row+2, col+2)) {
			if (list.doesExist(row+2, col+2) == false) {
				insert(row+2, col+2, player);
				availableJumps(list, row+2, col+2, player);
				remove(row+2, col+2);
			}
		}
		return true;
	}
	
	// Returns true or false depending if a piece at position [r1][c1] can jump to
	// position [r3][c3]. If it is possible, inserts an edge in the adjacency list
	// (indicates a jump move).
	private boolean canJump (AdjList list, int r1, int c1, int r2, int c2, int r3, 
			int c3) {
		
		if (r3 < 0 || r3 >= size || c3 < 0 || c3 >= size)
			return false; // (r3, c3) is off the board
		
		if (r2 < 0 || r2 >= size || c2 < 0 || c2 >= size)
			return false; // (r2, c2) is off the board
		
		if (board[r3][c3] != EMPTY)
			return false; // (r3, c3) already contains a piece
		
		if (board[r2][c2] == EMPTY || board[r2][c2] == DEAD)
			return false; // (r2, c2) is empty or a dead square
		
		// creates a node for the adjacency list (if it doesn't already exist)
		list.insert(r1, c1);	
		
		// creates edge between r1,c1 and r3,c3 (indicating jump move)
		list.insertNeighbour(r1, c1, r3, c3);
		return true;
	}
}


